package com.zmn.plat.admin.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zmn.plat.model.entity.channel.settle.ChannelMergeSettle;
import com.zmn.plat.services.interfaces.base.unit.UnitService;
import com.zmn.plat.services.interfaces.channel.settle.ChannelMergeSettleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.exception.ZmnException;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.manager.redis.RedisManager;
import com.zmn.mcc.cas.model.LoginStaff;
import com.zmn.mcc.cas.web.StaffAdminService;
import com.zmn.plat.constant.RedisKeyConstant;
import com.zmn.plat.services.interfaces.funswitch.PublicBaseCodeMapService;

/**
 * @author nowind
 * @since 17/4/8 11:23
 */
public class PlatBaseController {

    private static final Logger logger = LoggerFactory.getLogger(PlatBaseController.class);
    protected static final String TAG_BLACK = "IP白名单";

    @Resource
    RedisManager redisManager;

    /**
     * 默认排序值
     */
    protected static final int DEFAULT_SORT = 100;
    /**
     * 登录白名单缓存时间
     */
    protected static final int BLACK_CATCH_TIME = 120;
    /**
     * 导出excel没有配置限制数量导出失败errorCode
     */
    protected static final int EXPORT_LIMIT_ERROR_CODE = 555;

    /**
     * ip白名單信息接口
     */
    @Value("${zmn.blackwhite.info}")
    protected String blackWhiteApi;
    /**
     * 白名單接口調用周期
     */
    @Value("${zmn.blackwhite.peroid}")
    protected long blackWhitePeroid;
    /**
     * 特殊区域，无需检查
     */
    @Value("${zmn.blackwhite.nocheck.areaId}")
    protected String noCheckAreaId;

    @Value("${cas.blackwhite.name}")
    private String blackWhiteName;

    @Autowired
    private PublicBaseCodeMapService publishBaseCodeMapService;

    @Resource
    UnitService unitService;

    /**
     * 检查黑白名单功能是否开启 2 表示开启
     *
     * @return
     */
    public boolean isBlackWhiteIpListOn() {
        Integer blackWhiteStatus = publishBaseCodeMapService.getStatusByName(blackWhiteName);

        if (blackWhiteStatus != null && blackWhiteStatus == 2) {
            return true;
        }
        return false;
    }

    /**
     * 获取登录用户
     *
     * @return
     */
    public LoginStaff getMcStaff() {
        return StaffAdminService.currentStaff();
    }

    /**
     * 获取登录用户ID
     *
     * @param
     * @return
     */
    public int getMcStaffId() {
        LoginStaff staff = StaffAdminService.currentStaff();
        if(Objects.nonNull(staff)){
            return staff.getStaffId();
        }else {
            return 0;
        }
    }

    /**
     * 获取登录用户名
     *
     * @param
     * @return
     */
    public String getMcStaffRealName() {
        LoginStaff staff = StaffAdminService.currentStaff();
        if(Objects.nonNull(staff)){
            return staff.getRealName();
        }else {
            return "";
        }
    }

    /**
     * 获取登录姓名
     *
     * @param request
     * @return
     */
    public String getMcStaffName(HttpServletRequest request) {
        LoginStaff staff = StaffAdminService.currentStaff();
        if(Objects.nonNull(staff)){
            return staff.getStaffName();
        }else {
            return "";
        }
    }

    /**
     * 附加请求参数(日志拦截需要)
     *
     * @param request
     * @param moduleId
     *            集成管理系统中定义的模块ID
     * @param aboutId
     *            操作相关的主键ID，如skuId、categId等
     * @param aboutStatus
     *            操作相关记录的原始状态，如课程上下架状态
     * @param operatorType
     *            操作者类型，0系统 2运营 3机构 3用户
     */
    protected void setRequestAttributes(HttpServletRequest request, Integer moduleId, String aboutId, Integer aboutStatus,
        Integer operatorType) {

        request.setAttribute("moduleId", moduleId == null ? 0 : moduleId);
        request.setAttribute("aboutId", aboutId);
        request.setAttribute("aboutSatus", aboutStatus == null ? 0 : aboutStatus);
        request.setAttribute("operatorType", operatorType == null ? 0 : operatorType);
    }

    public ModelAndView ajaxDoneSuccess(String message) {
        return ajaxDone(200, message, "", "", "");
    }

    public ModelAndView ajaxDoneSuccess(String message, String navTabId) {
        return ajaxDone(200, message, navTabId, "", "");
    }

    public ModelAndView ajaxDoneSuccessAndUrl(String message, String url) {
        return ajaxDone(200, message, "", url, "forward");
    }

    public ModelAndView ajaxDoneSuccessAndUrl(String message, String navTabId, String url) {
        return ajaxDone(200, message, navTabId, url, "forward");
    }

    public ModelAndView ajaxDoneSuccessClose(String message) {
        return ajaxDone(200, message, "", "", "closeCurrent");
    }

    public ModelAndView ajaxDoneSuccessCloseAndNavTabId(String message, String navTabId) {
        return ajaxDone(200, message, navTabId, "", "closeCurrent");
    }

    public ModelAndView ajaxDoneSuccessCloseAndUrl(String message, String url) {
        return ajaxDone(200, message, "", url, "closeCurrent");
    }

    public ModelAndView ajaxDoneError(String message) {
        return ajaxDone(300, message, "", "", "");
    }

    public ModelAndView ajaxDone(int statusCode, String message, String navTabId, String forwardUrl, String callbackType) {

        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();

        if (StringUtil.isBlank(callbackType) && request.getParameter("callbackType") != null) {
            callbackType = request.getParameter("callbackType").toString();
        }
        if (StringUtil.isBlank(navTabId) && request.getParameter("navTabId") != null) {
            navTabId = request.getParameter("navTabId").toString();
        }
        if (StringUtil.isBlank(forwardUrl) && request.getParameter("forwardUrl") != null) {
            forwardUrl = request.getParameter("forwardUrl").toString();
        }

        String rel = "";
        if (request.getParameter("rel") != null) {
            rel = request.getParameter("rel").toString();
        }

        ModelAndView mav = new ModelAndView("ajaxDone");
        mav.addObject("statusCode", statusCode);
        mav.addObject("message", (!"forwardConfirm".equals(callbackType) ? message : ""));
        mav.addObject("confirmMsg", ("forwardConfirm".equals(callbackType) ? message : ""));
        mav.addObject("navTabId", navTabId);
        mav.addObject("rel", rel);
        mav.addObject("callbackType", callbackType);
        mav.addObject("forwardUrl", forwardUrl);
        return mav;
    }

    protected ModelAndView ajaxDoneAuto(String message) {
        if (StringUtil.isContains(message, "success")) {
            return ajaxDoneSuccess(getServiceConfig(message));
        } else if (StringUtil.isContains(message, "fail")) {
            return ajaxDoneError(getServiceConfig(message));
        } else {
            return ajaxDoneError(message);
        }
    }

    protected ModelAndView ajaxDoneAuto(String message, String forwardUrl) {
        if (StringUtil.isContains(message, "success")) {
            return ajaxDoneSuccessAndUrl(getServiceConfig(message), forwardUrl);
        } else if (StringUtil.isContains(message, "fail")) {
            return ajaxDoneError(getServiceConfig(message));
        } else {
            return ajaxDoneError(message);
        }
    }

    protected ModelAndView ajaxDoneAuto(List<String> messages) {

        if (messages.isEmpty() || messages.size() == 0) {
            return ajaxDoneError(getServiceConfig("msg.operation.fail"));
        }

        if (messages.size() == 1 && StringUtil.isContains(messages.get(0), "success")) {
            return ajaxDoneSuccess(messages.get(0));
        }

        String msg = "";
        for (int i = 1; i <= messages.size() - 1; i++) {
            msg += messages.get(i) + "<br/>";
        }

        String resultCode = messages.get(0);
        if (resultCode.equalsIgnoreCase(GlobalConsts.TRUE_STR)) {
            return ajaxDoneSuccess(msg);
        } else {
            return ajaxDoneError(msg);
        }
    }

    protected boolean isSuccess(List<String> messages) {
        if (messages.isEmpty() || messages.size() == 0) {
            return false;
        } else if (messages.size() == 1 && StringUtil.isContains(messages.get(0), "success")) {
            return true;
        } else if (messages.get(0).equalsIgnoreCase(GlobalConsts.TRUE_STR)) {
            return true;
        } else {
            return false;
        }
    }
    // endregion

    /**
     * 跳转登录页或管理中心首页
     *
     * @param message
     * @return
     */
    protected ModelAndView directToLoginMessage(String message, String redirectUrl) {
        ModelAndView model = directToLogin();
        model.addObject("message", message);
        model.addObject("redirectUrl", redirectUrl);
        return model;
    }

    protected ModelAndView directToLogin() {
        return directToLogin("");
    }

    protected ModelAndView directToLogin(String param) {
        String url = String.format("cas/login");
        if (StringUtil.isNotBlank(param)) {
            url += ("?" + param);
        }
        return new ModelAndView(url);
    }

    protected ModelAndView directToMc() {
        String url = String.format("redirect:/mc.action");
        return new ModelAndView(url);
    }

    protected ModelAndView directTo(String url) {
        url = String.format("redirect:%s", url);
        return new ModelAndView(url);
    }

    @Autowired
    private Environment environment;

    protected String getServiceConfig(String key) {
        return environment.getProperty(key);
    }

    /**
     * 附加请求参数(日志拦截需要)
     *
     * @param request
     * @param moduleId
     *            集成管理系统中定义的模块ID
     * @param aboutIds
     *            操作相关的主键ID，如skuId、categId等
     * @param aboutStatus
     *            操作相关记录的原始状态，如课程上下架状态
     */
    protected void setRequestAttributes(HttpServletRequest request, Integer moduleId, String aboutIds, Integer aboutStatus) {

    }

    protected void setRequestAttributes(HttpServletRequest request, Integer moduleId, Integer aboutId, Integer aboutStatus) {

        setRequestAttributes(request, moduleId, String.valueOf(aboutId), aboutStatus, GlobalDict.OPERATOR_TYPE_ADMIN);
    }

    /**
     * 解析登录白名单接口返回数据
     *
     * @param infosAllowToAccess
     */
    private Map<String, String> doParse(String infosAllowToAccess) {
        Map<String, String> ipAndAreaIds = new ConcurrentHashMap<>();

        logger.info("{}: 获取ip json串为: {}", TAG_BLACK, infosAllowToAccess);
        if (StringUtil.isBlank(infosAllowToAccess)) {
            return new ConcurrentHashMap<String, String>();
        }

        JSONObject parse = (JSONObject)JSONObject.parse(infosAllowToAccess);

        logger.info("{}: 解析ip json串为: {}", TAG_BLACK, parse);

        JSONArray data = parse.getJSONArray("data");

        if (ipAndAreaIds.size() > 0) {
            ipAndAreaIds.clear();
        }

        if (data.size() > 0) {
            for (int i = 0; i < data.size(); i++) {
                JSONObject job = data.getJSONObject(i);
                String id = job.getString("id");
                String areaId1 = job.getString("areaId");
                ipAndAreaIds.put(id, areaId1);
            }
        }

        return ipAndAreaIds;
    }

    /**
     * 初始化白名单数据
     */
    public Map<String, String> intialBlackWhiteList() {
        try {
            String res = sendGet(blackWhiteApi);

            if (StringUtil.isBlank(res)) {
                logger.error("{}: 初始化白名单数据：返回 Null, URL: {}", TAG_BLACK, blackWhiteApi);
                return new ConcurrentHashMap<>();
            }

            Map<String, String> ipAndAreaIds = PlatBaseController.this.doParse(res);

            try {
                redisManager.hmset(RedisKeyConstant.BLACKLIST_IP_LIST, ipAndAreaIds);
                redisManager.expire(RedisKeyConstant.BLACKLIST_IP_LIST, BLACK_CATCH_TIME);
            } catch (Exception e) {
                logger.error("{}: 缓存到Redis异常-{}", TAG_BLACK, e.getMessage());
            }

            return ipAndAreaIds;
        } catch (Exception e) {
            logger.error("{}: 调取ip黑白名单失败-{}", TAG_BLACK, e.getMessage());
        }

        return new ConcurrentHashMap<>();
    }

    /**
     * 检查ip，areaId是否在可登录列表内 若未获取到数据，则不进行黑白名单检测
     *
     * @param ip
     * @param staffAreaId
     * @return
     */
    protected boolean isIpAndAreaIdLegal(String ip, String staffAreaId) {
        Map<String, String> ipAndAreaIds = getIpAndAreaIds();

        if (ipAndAreaIds.size() == 0) {
            return true;
        }

        String areaId = ipAndAreaIds.get(ip);

        if (noCheckAreaId.equals(areaId)) {
            logger.info("{}: 当前登录用户ip为：{}, 此城市ID为全国城市, cityId:{}", TAG_BLACK, ip, areaId);
            return true;
        }

        if (null != areaId && areaId.equals(staffAreaId)) {
            return true;
        }

        return false;
    }

    protected Map<String, String> getIpAndAreaIds() {
        Map<Object, Object> ipAndAreas = redisManager.hgetall(RedisKeyConstant.BLACKLIST_IP_LIST);

        if (ipAndAreas == null || ipAndAreas.size() == 0) {
            return intialBlackWhiteList();
        }

        Map<String, String> map = new ConcurrentHashMap<>();

        ipAndAreas.forEach((key, value) -> {
            map.put(String.valueOf(key), String.valueOf(value));
        });

        return map;
    }

    public String sendGet(String url) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept",
                "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
            // 建立实际的连接
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            logger.info("发送GET请求出现异常！" + e.getMessage());
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                logger.info("HTTP请求后关闭流异常：{}", e2.getMessage());
            }
        }
        return result;
    }

    public static ResponseEntity tipMsg(String body) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("text/html;charset=utf-8"));
        headers.setAcceptCharset(Lists.newArrayList(Charset.forName("UTF-8")));
        return ResponseEntity.ok().headers(headers).body(body);
    }

    public static void tipMsg(HttpServletResponse response, String body) {
        response.setHeader("Content-Type", "text/html;charset=utf-8");
        try {
            PrintWriter writer = response.getWriter();
            writer.println(body);
            writer.flush();
        } catch (IOException e) {
            logger.error("error:", e);
        }
    }

    public static ResponseEntity alterMsgWithExportExcel() {
        return tipMsg("<script language='javascript'>alert('您没有配置导出数量，无法导出');</script>");
    }

    public static void alterMsgWithExportExcel(HttpServletResponse response) {
        tipMsg(response, "<script language='javascript'>alert('您没有配置导出数量，无法导出');</script>");
    }

    protected Integer getExportPageLimit(HttpServletResponse response) {
        Integer pageSize = getMcStaff().getExportLimit();
        if (NumberUtil.isNullOrZero(pageSize)) {
            try {
                response.setContentType("text/html;charset=utf-8");
                response.setStatus(EXPORT_LIMIT_ERROR_CODE);
                response.getWriter().print(EXPORT_LIMIT_ERROR_CODE);
                throw new ZmnException("未配置导出数量限制，导出失败");
            } catch (IOException e) {
                logger.error("", e);
            }
        }
        return pageSize;
    }

    /**
     * 描述: 查询所有单位名称
     *
     * @return List<String>
     * @author xujie
     * @since 2021-03-01 18:28:22
     **/
    public List<String> listAllUnitName() {

        return unitService.listAllUnitName();
    }

    @Resource
    protected ChannelMergeSettleService mergeSettleService;

    /**
     * 获取结算信息
     *
     * @param channelId 渠道id
     * @return 合并结算：1.否 2.是
     * @author xujie
     * @since 2021/04/14
     */
    protected Integer getMergeSettle(Integer channelId) {

        if (channelId == null) {
            return GlobalConsts.NO;
        }
        ChannelMergeSettle mergeSettle = mergeSettleService.findById(channelId);
        if (mergeSettle == null) {
            return GlobalConsts.NO;
        } else {
            return mergeSettle.getMergeSettle();
        }
    }
}
